﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlServerCe;
using System.Data.Linq;
using System.IO;
using System.Reflection;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls;
using System.Xml.Linq;

namespace MercuryFE
{
    static class DataLib
    {
        
        #region fields

        private static string[] _systemMameTypes = new string[] {
            "default","driver","rom"
        };
        #endregion


        private static string getCurrentDir()
        {
            return AppDomain.CurrentDomain.BaseDirectory.ToString();
        }


        private static string getConnectionString()
        {
            string currentDir = getCurrentDir();

            string connectionString = "Data Source=" + currentDir + (!currentDir.EndsWith("\\")?"\\":"") + "LauncherDB.sdf; Max Database Size=512";

            return connectionString;

            // temporary fix....
            //return @"Data Source=C:\Documents and Settings\horvathm\My Documents\Visual Studio 2008\Projects\Launcher\bin\Debug\LauncherDB.sdf; Max Database Size=512";
        }

        public static Dictionary<int, string> getEmulatorList()
        {
            Dictionary<int, string> emulatorList;

            using (SqlCeConnection ceConn = new SqlCeConnection(getConnectionString()))
            {

                ceConn.Open();
                using (LauncherDB db = new LauncherDB(ceConn))
                {
                    emulatorList =
                            (from x in db.Emulator
                             select new
                             {
                                 x.EmulatorID
                                 ,
                                 x.Name
                             }).ToDictionary(a => a.EmulatorID, b => b.Name);
                }

            }
            return emulatorList;
        }

        public static bool isEmulatorMame(int emulatorId)
        {
            bool isMame = false;
            using (SqlCeConnection ceConn = new SqlCeConnection(getConnectionString()))
            {

                ceConn.Open();
                using (LauncherDB db = new LauncherDB(ceConn))
                {
                    isMame =
                            (from x in db.Emulator
                             where x.EmulatorID == emulatorId
                             select ((int)x.IsMame==1?true:false)).SingleOrDefault();
                }

            }
            return isMame;
        }

        public static List<string> getMameDriverList()
        {
            List<string> drivers;
            using (SqlCeConnection ceConn = new SqlCeConnection(getConnectionString()))
            {

                ceConn.Open();
                using (LauncherDB db = new LauncherDB(ceConn))
                {
                    //drivers = (from x in db.MameRom
                    //             select Path.GetFileNameWithoutExtension(x.SourceFile)).Distinct().ToList();

                    drivers = (from mrb in db.MameRomBios
                              join mr in db.MameRom on mrb.BiosMameRomID equals mr.MameRomID
                              select mr.RomName).Distinct().ToList();
                }

            }
            return drivers;
        }

        public static List<string> getMameSystemTypes() {
            return _systemMameTypes.ToList();
        }

        public static List<string> getMameRomList()
        {
            List<string> roms;
            using (SqlCeConnection ceConn = new SqlCeConnection(getConnectionString()))
            {

                ceConn.Open();
                using (LauncherDB db = new LauncherDB(ceConn))
                {
                    roms = (from x in db.MameRom
                               select Path.GetFileNameWithoutExtension(x.RomName)).ToList();
                }

            }
            return roms;
        }

        public static Dictionary<int, string> getControlPanelAssignments(int controlPanelId)
        {
            Dictionary<int, string> assignments = new Dictionary<int,string>();
            using (SqlCeConnection ceConn = new SqlCeConnection(getConnectionString()))
            {

                ceConn.Open();
                using (LauncherDB db = new LauncherDB(ceConn))
                {
                    assignments = (
                        from cpa in db.ControlPanelAssignment
                        join e in db.Emulator on cpa.EmulatorID equals e.EmulatorID
                        where cpa.ControlPanelID == controlPanelId
                        select new
                        {
                            cpa.ControlPanelAssignmentID
                            ,
                            Description = e.Name + ((int)e.IsMame==1? " [" + cpa.SystemTag + "]":"")
                        }).ToDictionary(a => a.ControlPanelAssignmentID, b => b.Description);
                }

            }

            return assignments;
        }

        public static int saveControlPanelLayout(string panelName, ref List<ControlItem> items, int canvasWidth, int canvasHeight)
        {
            // go through all the control items and save them off

            // populate a lookup table to easily convert color value to name..
            Dictionary<string, string> colorLookup = new Dictionary<string, string>();
            PropertyInfo[] colors = typeof(Colors).GetProperties();
            foreach (PropertyInfo color in colors)
            {
                string value = color.GetValue(null, null).ToString();
                if (!colorLookup.ContainsKey(value))
                    colorLookup.Add(value, color.Name);
            }

            int controlPanelId;
            using (SqlCeConnection ceConn = new SqlCeConnection(getConnectionString()))
            {
                

                ceConn.Open();
                using (LauncherDB db = new LauncherDB(ceConn))
                {
                    //int controlPanelId = _controlPanelId;

                    ControlPanel cp = (from x in db.ControlPanel
                                       where x.PanelName == panelName
                                       select x).SingleOrDefault();

                    //if(cp_check == null)
                    //    _controlPanelId = null;
                    //else
                    //    _controlPanelId = cp_check.ControlPanelID;


                    int controlPanelWidth = canvasWidth;
                    int controlPanelHeight = canvasHeight;

                    // **** CONTROL PANEL
                    if (cp == null)
                    {
                        // have to create an entry ...
                        cp = new ControlPanel()
                        {
                            PanelName = panelName
                            ,
                            Height = controlPanelHeight
                            ,
                            Width = controlPanelWidth
                        };

                        db.ControlPanel.InsertOnSubmit(cp);
                        db.SubmitChanges();

                        //_controlPanelId = cp.ControlPanelID;
                    }
                    else
                    {
                        // otherwise update the existing record if required...
                        cp.Height = controlPanelHeight;
                        cp.Width = controlPanelWidth;
                        db.SubmitChanges();

                    }
                    controlPanelId = cp.ControlPanelID;

                    // delete records
                    // ids from document
                    int?[] currentIds = (from x in items
                                         where x.SourceID != null
                                         select x.SourceID).ToArray();

                    // list of items in the database that need to be cleared out (not on panel)
                    List<int> IdsToDelete = (from x in db.ControlPanelItem
                                             where x.ControlPanelID == cp.ControlPanelID // _controlPanelId
                                             && !currentIds.Contains(x.ControlPanelItemID)
                                             select x.ControlPanelItemID).ToList();

                    foreach (int id in IdsToDelete)
                    {
                        // delete any assignments
                        List<ControlPanelInputAssignment> assignments =
                            (from x in db.ControlPanelInputAssignment
                             join y in db.ControlPanelItemInput
                                on x.ControlPanelItemInputID equals y.ControlPanelItemInputID
                             where y.ControlPanelItemID == id
                             select x).ToList();

                        db.ControlPanelInputAssignment.DeleteAllOnSubmit(assignments);
                        db.SubmitChanges();


                        // then the inputs
                        List<ControlPanelItemInput> inputs = (from x in db.ControlPanelItemInput
                                                              where x.ControlPanelItemID == id
                                                              select x).ToList();

                        db.ControlPanelItemInput.DeleteAllOnSubmit(inputs);
                        db.SubmitChanges();

                        // then finally the item itself
                        ControlPanelItem item = (from x in db.ControlPanelItem
                                                 where x.ControlPanelItemID == id
                                                 select x).Single();
                        db.ControlPanelItem.DeleteOnSubmit(item);
                        db.SubmitChanges();
                    }



                    foreach (ControlItem item in items)
                    {
                        // **** CONTROL PANEL ITEM
                        // get x and y position

                        Point p = item.TranslatePoint(new Point(0.0, 0.0), (Canvas)item.Parent);




                        int? sourceId = item.SourceID;
                        string itemType = item.ItemType;
                        int posX = (int)p.X;
                        int posY = (int)p.Y;
                        string colorName = colorLookup[item.Brush.Color.ToString()];
                        string labelPosition = item.LabelPosition.ToString();
                        //string itemSubType = item.ItemSubType;

                        ControlPanelItem cpi;
                        // source id null, means it was added since last save.....
                        if (sourceId == null)
                        {

                            cpi = new ControlPanelItem()
                            {
                                ControlPanelID = cp.ControlPanelID // (int)_controlPanelId
                                 ,
                                Color = colorName
                                 ,
                                ItemType = itemType
                                 //,
                                // ItemSubType = itemSubType
                                 ,
                                LabelPosition = labelPosition
                                 ,
                                PosX = posX
                                 ,
                                PosY = posY
                            };

                            db.ControlPanelItem.InsertOnSubmit(cpi);
                            db.SubmitChanges();

                            // update the source id, once saved to the db
                            item.SourceID = cpi.ControlPanelItemID;

                        }
                        else
                        {
                            // find the previous value..

                            cpi = (from x in db.ControlPanelItem
                                   where x.ControlPanelItemID == sourceId
                                   select x).Single();

                            // only thing that would change is color, label pos, or x/y pos
                            bool hasChanged = false;
                            if (cpi.Color != colorName)
                            {
                                cpi.Color = colorName;
                                hasChanged = true;
                            }
                            if (cpi.LabelPosition != labelPosition)
                            {
                                cpi.LabelPosition = labelPosition;
                                hasChanged = true;
                            }
                            if (cpi.PosX != posX)
                            {
                                cpi.PosX = posX;
                                hasChanged = true;
                            }
                            if (cpi.PosY != posY)
                            {
                                cpi.PosY = posY;
                                hasChanged = true;
                            }
                            if (hasChanged)
                            {
                                db.SubmitChanges();
                            }
                        }



                        // **** CONTROL PANEL ITEM INPUTS
                        foreach (ControlItemInput input in item.Inputs)
                        {
                            ControlPanelItemInput cpii;
                            if (input.SourceID == null)
                            {
                                // new item
                                cpii = new ControlPanelItemInput()
                                {
                                    ControlPanelItemID = cpi.ControlPanelItemID
                                     ,
                                    InputType = input.Name
                                     ,
                                    KeyCode = input.KeyCode
                                };
                                db.ControlPanelItemInput.InsertOnSubmit(cpii);
                                db.SubmitChanges();

                                // update source id from value generated in the db
                                input.SourceID = cpii.ControlPanelItemInputID;
                            }
                            else
                            {
                                cpii = (from x in db.ControlPanelItemInput
                                        where x.ControlPanelItemInputID == input.SourceID
                                        select x).Single();

                                cpii.KeyCode = input.KeyCode;
                                db.SubmitChanges();


                            }
                        }

                    }







                }
            }
            return controlPanelId;
        }

        public static bool loadControlPanelLayout(string panelName, ref List<ControlItem> items, ref int canvasWidth, ref int canvasHeight)
        {
            using (SqlCeConnection ceConn = new SqlCeConnection(getConnectionString()))
            {
                ceConn.Open();
                using (LauncherDB db = new LauncherDB(ceConn))
                {
                    ControlPanel cp = (from x in db.ControlPanel
                                       where x.PanelName == panelName
                                       select x).SingleOrDefault();


                    //_controlPanelId = (from x in db.ControlPanel
                    //                   where x.PanelName == _defaultPanelName
                    //                     select x.ControlPanelID).SingleOrDefault();

                    if (cp != null)
                    {
                        canvasWidth = cp.Width;
                        canvasHeight = cp.Height;

                        items = (from cpi in db.ControlPanelItem
                                 where cpi.ControlPanelID == cp.ControlPanelID // _controlPanelId
                                 select new ControlItem((ControlItemType)Enum.Parse(typeof(ControlItemType), cpi.ItemType))
                                 {
                                     //ItemType = cpi.ItemType
                                     Brush = new SolidColorBrush((Color)ColorConverter.ConvertFromString(cpi.Color))
                                      ,
                                     IsSelected = false
                                      ,
                                     SourceID = cpi.ControlPanelItemID
                                      ,
                                     LabelPosition = (LabelPositionType)Enum.Parse(typeof(LabelPositionType), cpi.LabelPosition)
                                      ,
                                     Position = new Point(cpi.PosX, cpi.PosY)
                                      ,
                                     Inputs =
                                         (
                                           from cpii in db.ControlPanelItemInput
                                           where cpii.ControlPanelItemID == cpi.ControlPanelItemID
                                           select new ControlItemInput(cpii.InputType, cpii.KeyCode, cpii.ControlPanelItemInputID)
                                         ).ToList()
                                 }).ToList();
                        return true;
                    }
                    else
                    {
                        // no control panel found
                        return false;
                    }
                }
            }
        }


        public static int createControlPanelAssignment(int controlPanelId, int emulatorId, string systemTag)
        {
            int controlPanelAssignmentId = -1;
            using (SqlCeConnection ceConn = new SqlCeConnection(getConnectionString()))
            {
                ceConn.Open();
                using (LauncherDB db = new LauncherDB(ceConn))
                {
                    ControlPanelAssignment cpa = new ControlPanelAssignment()
                    {
                        ControlPanelID = controlPanelId
                        ,
                        EmulatorID = emulatorId
                        ,
                        SystemTag = systemTag
                    };
                    db.ControlPanelAssignment.InsertOnSubmit(cpa);
                    db.SubmitChanges();
                    controlPanelAssignmentId = cpa.ControlPanelAssignmentID;
                }


            }
            return controlPanelAssignmentId;
        }

        public static void loadControlPanelAssignment(int controlPanelAssignmentId, ref List<ControlItem> items, ref bool isMame, ref int? emulatorId)
        {

            // clear out all the current assignments
            foreach (ControlItem item in items)
            {
                foreach (ControlItemInput input in item.Inputs)
                    input.Assignment = null;
            }

            // now load the available assignments from the DB, and match to relevant object
            using (SqlCeConnection ceConn = new SqlCeConnection(getConnectionString()))
            {
                ceConn.Open();
                using (LauncherDB db = new LauncherDB(ceConn))
                {

                    // check if this assignment is for mame
                    var check = (from cpa in db.ControlPanelAssignment
                                join e in db.Emulator on cpa.EmulatorID equals e.EmulatorID
                                where cpa.ControlPanelAssignmentID == controlPanelAssignmentId
                                select new {
                                    isMame = (e.IsMame == 1 ? true : false)
                                    , e.EmulatorID
                                }).FirstOrDefault();

                    isMame = check.isMame;
                    emulatorId = check.EmulatorID;

                    List<ControlPanelInputAssignment> assignments = (from x in db.ControlPanelInputAssignment
                                 where x.ControlPanelAssignmentID == controlPanelAssignmentId
                                 select x).ToList();




                    


                    //bool processedMain = false;
                    foreach (ControlPanelInputAssignment assignment in assignments)
                    {
                        // find the right record in the control Item input
                        ControlItemInput cpi = (from x in items
                                    from y in x.Inputs
                                    where y.SourceID == assignment.ControlPanelItemInputID
                                    select y).SingleOrDefault();

                        //if (!processedMain && cpi != null)
                        //{
                        //    ControlItem ci = (from x in items
                        //                      from y in x.Inputs
                        //                      where y.SourceID == assignment.ControlPanelItemInputID
                        //                      select x).SingleOrDefault();

                        //    // essentially, grab the first assignment type from the inputs, and put this up to control item level
                        //    ci.AssignmentType = assignment.AssignmentType;
                        //    processedMain = true;
                        //}

                        if (cpi != null)
                        {
                            cpi.Assignment = assignment.AssignmentTag;
                            cpi.AssignmentType = assignment.AssignmentType;
                        }

                    }

                    var query = (from x in items
                                 from y in x.Inputs
                                 where x.AssignmentType != y.AssignmentType
                                 select new { item = x, y.AssignmentType }).Distinct().ToList();

                    foreach (var obj in query)
                    {
                        obj.item.AssignmentType = obj.AssignmentType;
                    }
                }

            }

        }

        public static void deleteControlPanelAssignment(int controlPanelAssignmentId)
        {
            using (SqlCeConnection ceConn = new SqlCeConnection(getConnectionString()))
            {
                ceConn.Open();
                using (LauncherDB db = new LauncherDB(ceConn))
                {
                    // clear out input assignments
                    var inputAssignments = from cpia in db.ControlPanelInputAssignment
                                where cpia.ControlPanelAssignmentID == controlPanelAssignmentId
                                select cpia;

                    db.ControlPanelInputAssignment.DeleteAllOnSubmit(inputAssignments);
                    db.SubmitChanges();

                    // now the assignment record

                    var assignment = from cpa in db.ControlPanelAssignment
                                     where cpa.ControlPanelAssignmentID == controlPanelAssignmentId
                                     select cpa;

                    db.ControlPanelAssignment.DeleteAllOnSubmit(assignment);
                    db.SubmitChanges();

                }
            }
        }

        public static void saveControlPanelAssignment(int controlPanelAssignmentId, List<ControlItem> items)
        {

            // push assignment type down from item level to input level
            foreach (ControlItem item in items)
            {
                foreach (ControlItemInput input in item.Inputs)
                {
                    input.AssignmentType = item.AssignmentType;
                }
            }

            using (SqlCeConnection ceConn = new SqlCeConnection(getConnectionString()))
            {
                ceConn.Open();
                using (LauncherDB db = new LauncherDB(ceConn))
                {

                    foreach (ControlItem item in items)
                    {
                        foreach (ControlItemInput input in item.Inputs)
                        {
                            // find assignment based on itemInputID and AssignmentID
                            ControlPanelInputAssignment cpia = (from x in db.ControlPanelInputAssignment
                                        where x.ControlPanelItemInputID == input.SourceID
                                        && x.ControlPanelAssignmentID == controlPanelAssignmentId
                                        select x).SingleOrDefault();


                            // nothing has been assigned on panel....
                            if (input.Assignment == null)
                            {
                                // but one exists in the db
                                if (cpia != null)
                                {
                                    // delete it..
                                    db.ControlPanelInputAssignment.DeleteOnSubmit(cpia);
                                    db.SubmitChanges();
                                }

                            }
                            else
                            {
                                // panel has the input assigned... will need to save..

                                // if found, check if we need to update..
                                if (cpia != null)
                                {
                                    if (cpia.AssignmentTag != input.Assignment || cpia.AssignmentType != input.AssignmentType)
                                    {
                                        cpia.AssignmentTag = input.Assignment;
                                        cpia.AssignmentSubType = input.AssignmentSubType;
                                        cpia.AssignmentType = input.AssignmentType;
                                        db.SubmitChanges();
                                    }

                                }
                                else
                                {
                                    // need to create a new entry
                                    cpia = new ControlPanelInputAssignment()
                                    {
                                        ControlPanelAssignmentID = controlPanelAssignmentId
                                         ,
                                        ControlPanelItemInputID = (int)input.SourceID
                                         ,
                                        AssignmentTag = input.Assignment
                                         ,
                                        AssignmentType = input.AssignmentType ?? "custom"  
                                        ,
                                        AssignmentSubType = input.AssignmentSubType ?? "KB"
                                    };
                                    db.ControlPanelInputAssignment.InsertOnSubmit(cpia);
                                    db.SubmitChanges();
                                }

                            }

                        }
                    }
                }
            }


        }


        public static void generateMameConfig(string fileName, int emulatorID, int controlPanelID)
        {



            using (SqlCeConnection ceConn = new SqlCeConnection(getConnectionString()))
            {
                ceConn.Open();
                using (LauncherDB db = new LauncherDB(ceConn))
                {

                    string EmulatorName = (from e in db.Emulator where e.EmulatorID == emulatorID select e.Name).FirstOrDefault();

                    //string saveFileName = EmulatorName + ".cfg";

                    // grab list of assignments, in correct order, to put into config file...
                    var assignments = (from cpa in db.ControlPanelAssignment
                                       join e in db.Emulator on cpa.EmulatorID equals e.EmulatorID
                                       join mr in db.MameRom on cpa.SystemTag equals mr.RomName into mr_check
                                       where e.EmulatorID == emulatorID && cpa.ControlPanelID == controlPanelID
                                       from mrc in mr_check.DefaultIfEmpty()
                                       orderby ((cpa.SystemTag == "default" ? 1 : (mrc.RomName == null ? 2 : 3))) // make sure default first, then drivers, finally roms
                                       select new
                                       {
                                           SystemTag = cpa.SystemTag
                                           ,
                                           ControlPanelAssignmentID = cpa.ControlPanelAssignmentID
                                       }).ToList();


                    // load key maps (to convert key codes to the Mame representations)
                    string keyFile = getCurrentDir() + "\\DataFiles\\keys.xml";
                    var KeyMapDoc = XElement.Load(keyFile);

                    var keyCodeLookup = (from k in KeyMapDoc.Descendants("key")
                                         select new
                                         {
                                             code = (string)k.Attribute("code")
                                             ,
                                             mameCode = (string)k.Attribute("mameKey")
                                         }).ToDictionary(a => a.code, b => b.mameCode);




                    XElement doc = new XElement("mameconfig", new XAttribute("version", "10"));
                    doc.Add(new XComment("Generated by MercuryFE [" + DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString() + "]"));

                    foreach (var assignment in assignments)
                    {

                        XElement systemElement = new XElement("system", new XAttribute("name", assignment.SystemTag));

                        var inputAssignments = (from cpii in db.ControlPanelItemInput
                                                join cpi in db.ControlPanelItem on cpii.ControlPanelItemID equals cpi.ControlPanelItemID
                                                join cpia in db.ControlPanelInputAssignment on cpii.ControlPanelItemInputID equals cpia.ControlPanelItemInputID
                                                into cpia_c
                                                from cpiac in cpia_c.DefaultIfEmpty()
                                                where
                                                    cpi.ControlPanelID == controlPanelID
                                                && cpiac.ControlPanelAssignmentID == assignment.ControlPanelAssignmentID
                                                //			&& (
                                                //				cpiac.ControlPanelAssignmentID == assignment.ControlPanelAssignmentID
                                                //				|| cpia.ControlPanelAssignmentID == null)

                                                select new
                                                {
                                                    cpii.InputType
                                                    ,
                                                    cpii.KeyCode
                                                    ,
                                                    cpiac.AssignmentTag
                                                }).ToList();


                        // remove duplicate assignments, by updating value to include 'OR'
                        Dictionary<string, string> cleanedAssignments = new Dictionary<string, string>();

                        foreach (var inputAssignment in inputAssignments)
                        {
                            if (!cleanedAssignments.ContainsKey(inputAssignment.AssignmentTag))
                            {
                                cleanedAssignments.Add(inputAssignment.AssignmentTag, keyCodeLookup[inputAssignment.KeyCode]);
                            }
                            else
                            {
                                string currentvalue = cleanedAssignments[inputAssignment.AssignmentTag];
                                cleanedAssignments.Remove(inputAssignment.AssignmentTag);

                                currentvalue += " OR " + keyCodeLookup[inputAssignment.KeyCode];
                                cleanedAssignments.Add(inputAssignment.AssignmentTag, currentvalue);
                            }
                        }

                        


                        //foreach (var inputAssignment in inputAssignments)
                        foreach(KeyValuePair<string,string> cleanedAssignment in cleanedAssignments.OrderBy(x => x.Key)) 
                        {
                            XElement inputElement = new XElement("input",
                                new XElement("port",
                                    new XAttribute("type", cleanedAssignment.Key)
                                    , new XElement("newseq"
                                        , new XAttribute("type", "standard")
                                        , cleanedAssignment.Value
                                        )
                                    )
                                );

                            systemElement.Add(inputElement);
                        }
                        doc.Add(systemElement);
                    }

                    // finally, write the file out..
                    File.WriteAllText(fileName, doc.ToString());
                }
                	
            }
        
           	
        }


        //public static void deleteControlPanelAssignment(int controlPanelAssignmentId, ref List<ControlItem> items)
        //{
        //    // clear out all the current assignments
        //    foreach (ControlItem item in items)
        //    {
        //        foreach (ControlItemInput input in item.Inputs)
        //            input.Assignment = null;
        //    }

        //    using (SqlCeConnection ceConn = new SqlCeConnection(getConnectionString()))
        //    {
        //        ceConn.Open();
        //        using (LauncherDB db = new LauncherDB(ceConn))
        //        {
        //            // clear out input assignments first..
        //            List<ControlPanelInputAssigment> assignments = (from x in db.ControlPanelInputAssigment
        //                        where x.ControlPanelAssignmentID == controlPanelAssignmentId
        //                        select x).ToList();

        //            db.ControlPanelInputAssigment.DeleteAllOnSubmit(assignments);
        //            db.SubmitChanges();

        //            // now the assignment itself
        //            ControlPanelAssigment cpa = (from x in db.ControlPanelAssigment
        //                                        where x.ControlPanelAssignmentID == controlPanelAssignmentId
        //                                        select x).SingleOrDefault();
        //            db.ControlPanelAssigment.DeleteOnSubmit(cpa);
        //            db.SubmitChanges();
        //        }

        //    }

        //}

    }
}
